﻿using System;
using System.Diagnostics;
using System.Reflection;
using Kaleida.ServiceMonitor.Framework;
using Kaleida.ServiceMonitor.Model.Runtime;
using NUnit.Framework;

namespace Kaleida.UnitTests.Model.Runtime
{
    [TestFixture]
    public class ReflectionOperationFactoryTests
    {
        [Test]
        public void TestBuildRequest()
        {
            IPreparedRequestFactory factory = new ReflectionOperationFactory(new[] {Assembly.GetExecutingAssembly()});
            var request = factory.Build("my-first-request", new[] {"1", "2"});
            Assert.AreEqual("a=1, b=2", request.Description);
        }

        [Test]
        [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Cannot find a PreparedRequest implementation for 'bad-request' within these search assemblies:\r\n  • Kaleida.ServiceMonitor.UnitTests, Version=[0-9]+.[0-9]+.[0-9]+.[0-9]+, Culture=neutral, PublicKeyToken=null\r\n\r\nThe .NET implementation must be named 'BadRequest', derive from PreparedRequest, have public visibility and a public constructor containing all string parameters", MatchType = MessageMatch.Regex)]
        public void TestExceptionIsThrownWhenBuildRequestWithInvalidName()
        {
            IPreparedRequestFactory factory = new ReflectionOperationFactory(new[] { Assembly.GetExecutingAssembly() });
            factory.Build("bad-request", new[] {""});
        }

        [Test]
        [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Cannot find a PreparedRequest implementation for 'my-first-request' within these search assemblies:\r\n  • Kaleida.ServiceMonitor.UnitTests, Version=[0-9]+.[0-9]+.[0-9]+.[0-9]+, Culture=neutral, PublicKeyToken=null\r\n\r\nThe .NET implementation must be named 'MyFirstRequest', derive from PreparedRequest, have public visibility and a public constructor containing all string parameters", MatchType = MessageMatch.Regex)]
        public void TestExceptionIsThrownWhenBuildRequestWithTooFewArguments()
        {
            IPreparedRequestFactory factory = new ReflectionOperationFactory(new[] { Assembly.GetExecutingAssembly() });
            factory.Build("my-first-request", new[] { "1" });
        }

        [Test]
        [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Cannot find a PreparedRequest implementation for 'my-first-request' within these search assemblies:\r\n  • Kaleida.ServiceMonitor.UnitTests, Version=[0-9]+.[0-9]+.[0-9]+.[0-9]+, Culture=neutral, PublicKeyToken=null\r\n\r\nThe .NET implementation must be named 'MyFirstRequest', derive from PreparedRequest, have public visibility and a public constructor containing all string parameters", MatchType = MessageMatch.Regex)]
        public void TestExceptionIsThrownWhenBuildRequestWithTooManyArguments()
        {
            IPreparedRequestFactory factory = new ReflectionOperationFactory(new[] { Assembly.GetExecutingAssembly() });
            factory.Build("my-first-request", new[] { "1", "2", "3" });
        }

        [Test]
        [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Cannot find a PreparedRequest implementation for 'bad-request'. There are no operation definition assemblies available", MatchType = MessageMatch.Regex)]
        public void TestExceptionIsThrownWhenBuildRequestAndNoAssembliesProvided()
        {
            IPreparedRequestFactory factory = new ReflectionOperationFactory(new Assembly[0]);
            factory.Build("bad-request", new[] { "" });
        }


        
        [Test]
        public void TestBuildHandler()
        {
            IResponseHandlerFactory factory = new ReflectionOperationFactory(new[] { Assembly.GetExecutingAssembly() });
            var request = factory.Build("my-first-handler", new[] { "1", "2" });
            Assert.AreEqual("a=1, b=2", request.Description);
        }

        [Test]
        [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Cannot find a ResponseHandler implementation for 'bad-handler' within these search assemblies:\r\n  • Kaleida.ServiceMonitor.UnitTests, Version=[0-9]+.[0-9]+.[0-9]+.[0-9]+, Culture=neutral, PublicKeyToken=null\r\n\r\nThe .NET implementation must be named 'BadHandler', derive from ResponseHandler, have public visibility and a public constructor containing all string parameters", MatchType = MessageMatch.Regex)]
        public void TestExceptionIsThrownWhenBuildHandlerWithInvalidName()
        {
            IResponseHandlerFactory factory = new ReflectionOperationFactory(new[] { Assembly.GetExecutingAssembly() });
            factory.Build("bad-handler", new[] { "" });
        }

        [Test]
        [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Cannot find a ResponseHandler implementation for 'my-first-handler' within these search assemblies:\r\n  • Kaleida.ServiceMonitor.UnitTests, Version=[0-9]+.[0-9]+.[0-9]+.[0-9]+, Culture=neutral, PublicKeyToken=null\r\n\r\nThe .NET implementation must be named 'MyFirstHandler', derive from ResponseHandler, have public visibility and a public constructor containing all string parameters", MatchType = MessageMatch.Regex)]
        public void TestExceptionIsThrownWhenBuildHandlerWithTooFewArguments()
        {
            IResponseHandlerFactory factory = new ReflectionOperationFactory(new[] { Assembly.GetExecutingAssembly() });
            factory.Build("my-first-handler", new[] { "1" });
        }

        [Test]
        [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Cannot find a ResponseHandler implementation for 'my-first-handler' within these search assemblies:\r\n  • Kaleida.ServiceMonitor.UnitTests, Version=[0-9]+.[0-9]+.[0-9]+.[0-9]+, Culture=neutral, PublicKeyToken=null\r\n\r\nThe .NET implementation must be named 'MyFirstHandler', derive from ResponseHandler, have public visibility and a public constructor containing all string parameters", MatchType = MessageMatch.Regex)]
        public void TestExceptionIsThrownWhenBuildHandlerWithTooManyArguments()
        {
            IResponseHandlerFactory factory = new ReflectionOperationFactory(new[] { Assembly.GetExecutingAssembly() });
            factory.Build("my-first-handler", new[] { "1", "2", "3" });
        }

        [Test]
        [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Cannot find a ResponseHandler implementation for 'my-first-handler'. There are no operation definition assemblies available", MatchType = MessageMatch.Regex)]
        public void TestExceptionIsThrownWhenBuildHandlerAndNoAssembliesProvided()
        {
            IResponseHandlerFactory factory = new ReflectionOperationFactory(new Assembly[0]);
            factory.Build("my-first-handler", new[] { "" });
        }

        [Test]
        public void TestGetCriteriaAnalysis()
        {
            var analysis1 = ReflectionOperationFactory.GetCriteriaAnalysis(typeof (MyFirstRequest), "my-first-request", new[] {"a"}, typeof (DateTime));
            Trace.WriteLine(analysis1);
            Assert.AreEqual(
@"  Kaleida.UnitTests.Model.Runtime.ReflectionOperationFactoryTests+MyFirstRequest:
    Has correct name? ☑
    Derives from System.DateTime? ☐
    Publicly visibile? ☑
    Public constructor with 1 string parameter(s)? ☐
", analysis1);
        }

        public class MyFirstRequest : PreparedRequest
        {
            private readonly string a;
            private readonly string b;

            public MyFirstRequest(string a, string b)
            {
                this.a = a;
                this.b = b;
            }

            public override string Description
            {
                get { return string.Format("a={0}, b={1}", a, b); }
            }

            public override object GetResponse()
            {
                return null;
            }
        }

        public class MyFirstHandler : ResponseHandler
        {
            private readonly string a;
            private readonly string b;

            public MyFirstHandler(string a, string b)
            {
                this.a = a;
                this.b = b;
            }

            public override string Description
            {
                get { return string.Format("a={0}, b={1}", a, b); }
            }

            public override string ProcessResponse(object response)
            {
                return null;
            }
        }

    }
}
